﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace ArduinoInterOp
{
    public class ArduinoPinOperationData
    {
        private ushort _value;

        public ArduinoCommands CommandType { get; set; }
        public byte Pin { get; set; }
        public ushort Value
        {
            get { return _value; }
            set
            {
                if (value > 1023)
                {
                    //TODO: More informative
                    throw new ArgumentOutOfRangeException();
                }

                _value = value;
            }
        }

        internal ArduinoPinOperationData()
        { }

        public ArduinoPinOperationData(ArduinoCommands commandType, byte pin)
            :this(commandType, pin, null)
        {
        }

        public ArduinoPinOperationData(ArduinoCommands commandType, byte pin, ushort? value)
        {
            if (commandType == ArduinoCommands.SetOutputPinValue && !value.HasValue)
            {
                //TODO: More informative
                throw new ArgumentException("");
            }
            CommandType = commandType;
            Pin = pin;
            Value = value.HasValue ? value.Value : ushort.MinValue;
        }

        internal string ToNumericRepresentationString()
        {
            int commandNumber = 0;

            commandNumber |= (byte)CommandType;
            commandNumber = commandNumber << 8;
            commandNumber |= Pin;
            commandNumber = commandNumber << 10;
            commandNumber |= (1023 & Value);
            commandNumber = commandNumber << 38;

            return commandNumber.ToString(CultureInfo.InvariantCulture);
        }

        internal void ParseNumericRepresentationString(string numericRepresentationString)
        {
            int numericRep;
            if(int.TryParse(numericRepresentationString, out numericRep))
            {
                this.CommandType = GetCommand(numericRep);
                this.Pin = GetPin(numericRep);
                this.Value = GetValue(numericRep);
            }
            else
            {
                //TODO:
                throw new FormatException();
            }
        }

        private ushort GetValue(int responseData)
        {
            return Convert.ToUInt16(responseData >> 6 & 1023);
        }

        private byte GetPin(int responseData)
        {
            return Convert.ToByte((responseData >> 16) & byte.MaxValue);
        }

        private ArduinoCommands GetCommand(int responseData)
        {
            return (ArduinoCommands)Convert.ToByte((responseData >> 24) & byte.MaxValue);
        }
    }
}
